05 网络与存储
容器默认是相互隔离的。如果Agent容器和Redis容器不在同一个网络中,它们就无法直接通信。另一个常见问题是:容器删除后,容器内部的数据也会随之消失,例如对话记录或数据库文件。
Docker网络用于处理容器通信,Docker存储用于处理数据持久化。
一、端口映射
容器有自己独立的网络命名空间,默认和外部隔离。要从宿主机访问容器里的服务,需要做端口映射。
1.1 基本用法
# 宿主机8080端口 → 容器80端口
docker run -d -p 8080:80 nginx格式是-p 宿主机端口:容器端口。这时访问http://localhost:8080,请求就会被转发到容器的80端口。
1.2 EXPOSE vs 端口映射
Dockerfile里的EXPOSE只是声明,不会实际发布端口。要从外部访问,必须在docker run时用-p映射,或者在compose.yaml里配置ports。
# compose.yaml
services:
web:
ports:
- "8000:5000"1.3 端口映射注意事项
映射端口时需要注意以下几点:
- 一个宿主机端口只能映射给一个容器
- 不指定宿主机端口让Docker自动分配:
-p 80(容器80端口映射到宿主机的随机端口) - 绑定到特定网卡:
-p 127.0.0.1:8080:80(只允许本机访问)
安全提醒: 端口映射默认绑定到所有网卡(0.0.0.0),意味着任何能访问宿主机的人都可能访问这个端口。数据库这类敏感服务不要直接暴露端口;确实需要映射时,也应尽量绑定到127.0.0.1。
二、容器网络
2.1 默认网络
Docker安装后会创建一个默认的bridge网络。直接用docker run启动的容器,默认都会连接到这个网络。
# 查看Docker网络列表
docker network ls输出类似:
NETWORK ID NAME DRIVER SCOPE
a1b2c3d4e5f6 bridge bridge local
f6e5d4c3b2a1 host host local
1a2b3c4d5e6f none null localDocker内置网络如下:
| 网络 | 说明 |
|---|---|
| bridge | 默认网络,容器之间通过虚拟网桥通信 |
| host | 容器直接使用宿主机的网络栈,没有隔离 |
| none | 没有网络,容器完全断网 |
2.2 容器间通信
在同一个bridge网络中,容器之间可以用容器名作为主机名互相访问。
# 创建自定义网络
docker network create my-network
# 启动Redis,加入my-network
docker run -d --name redis --network my-network redis:alpine
# 启动Agent服务,加入my-network
docker run -d --name agent --network my-network my-agent:1.0在Agent容器中,可以直接用redis这个主机名连接Redis:
import redis
r = redis.Redis(host='redis', port=6379)使用Compose时通常不需要手动创建网络。 Compose会自动创建一个默认网络,所有服务都会加入进去,服务名就是主机名。
2.3 网络别名
一个容器也可以配置多个网络别名:
docker run -d --network my-network --network-alias db --network-alias database postgres其他容器可以用db或database访问这个容器。
三、数据持久化
容器被删除后,容器内部的数据也会被删除。例如在容器中运行Redis并写入数据,执行docker rm删除容器后,这些数据也会消失。
Docker提供了两种常用的数据持久化方式:Volume(数据卷)和Bind Mount(绑定挂载)。
3.1 Volume(数据卷)
Volume是Docker管理的存储区域,它独立于容器生命周期。容器删除后,Volume仍然保留。
# 创建Volume
docker volume create my-data
# 启动容器并挂载Volume
docker run -d -v my-data:/data redis:alpine-v my-data:/data表示把my-data这个Volume挂载到容器的/data目录。容器写入/data的数据,实际会保存到Volume中。
验证数据持久化:
# 启动Redis并写入数据
docker run -d --name redis1 -v redis-data:/data redis:alpine
docker exec redis1 redis-cli SET mykey "hello"
# 删除容器
docker rm -f redis1
# 用同一个Volume启动新容器
docker run -d --name redis2 -v redis-data:/data redis:alpine
docker exec redis2 redis-cli GET mykey
# 输出: "hello"数据仍然存在,因为Volume没有随容器一起删除。
3.2 Volume管理
# 列出所有Volume
docker volume ls
# 查看Volume详情
docker volume inspect my-data
# 删除指定Volume
docker volume rm my-data
# 删除所有未使用的Volume
docker volume prune在Compose里使用Volume:
services:
redis:
image: redis:alpine
volumes:
- redis-data:/data
# 声明Volume
volumes:
redis-data:docker compose down不会删除Volume,数据会保留。docker compose down -v才会同时删除Volume。
3.3 Bind Mount(绑定挂载)
Bind Mount会把宿主机上的目录直接挂载到容器里。和Volume不同,Bind Mount中的文件直接存放在宿主机上,可以用编辑器直接修改。
# 把当前目录挂载到容器的/app
docker run -d -v $(pwd):/app my-app:1.0Bind Mount适合开发场景:宿主机上的代码修改后,容器内可以立即看到变化。但它不适合生产环境,因为它依赖宿主机上的具体目录结构。
在Compose里使用Bind Mount:
services:
web:
build: .
volumes:
- ./src:/app/src # 绑定挂载
- redis-data:/data # 命名卷3.4 Volume vs Bind Mount
| 对比项 | Volume | Bind Mount |
|---|---|---|
| 管理方 | Docker管理 | 用户管理 |
| 存储位置 | Docker数据目录 | 宿主机任意目录 |
| 可移植性 | 高(不依赖宿主机路径) | 低(依赖宿主机路径) |
| 适合场景 | 数据持久化(数据库、日志) | 开发时挂载代码 |
| 性能 | 好 | Linux上好,Mac/Windows上有性能问题 |
生产环境优先用Volume,开发环境常用Bind Mount。
四、网络和存储的组合
结合网络和存储,一个Agent服务可以这样组织:
services:
web:
build: .
ports:
- "8000:5000"
environment:
- REDIS_HOST=redis
volumes:
- app-logs:/app/logs
depends_on:
redis:
condition: service_healthy
redis:
image: redis:alpine
volumes:
- redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
volumes:
redis-data:
app-logs:该配置包含以下内容:
- web和redis在同一个网络里,web用
redis主机名访问redis - web的5000端口映射到宿主机的8000端口
- redis的数据持久化到
redis-data卷 - web的日志持久化到
app-logs卷 - 健康检查保证redis就绪后再启动web
五、总结
| 概念 | 作用 |
|---|---|
端口映射(-p) | 从宿主机访问容器服务 |
| bridge网络 | 默认网络,容器间用服务名通信 |
| Volume | Docker管理的数据持久化,适合生产 |
| Bind Mount | 宿主机目录直接挂载,适合开发 |
depends_on + healthcheck | 控制启动顺序,保证依赖就绪 |
实际使用时,可以遵循以下原则:
- 端口映射要谨慎——敏感服务不要直接暴露,或者绑定到
127.0.0.1 - 数据要持久化——数据库、日志等数据要用Volume,否则容器删除后数据也会丢失
- 生产用Volume,开发用Bind Mount
后续实战会把前面的概念串联起来,部署一个完整的AI Agent服务。